﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codemonk.Common.Caching;

namespace Codemonk.Common.Injection
{
    internal sealed class TypeDictionary<TValue> : ICacheCollection<Type, TValue>
    {
        private ICacheCollection<Type, TValue> cache = new DictionaryCache<Type, TValue>();

        public void Clear()
        {
            cache.Clear();
        }

        public IEnumerable<Type> Keys
        {
            get { return cache.Keys; }
        }

        public int Count
        {
            get { return cache.Count; }
        }

        public bool ContainsKey(Type key)
        {
            return cache.ContainsKey(key);
        }

        public void Set(Type key, TValue value)
        {
            if (key == null) return;
            cache.Set(key, value);
            List<Type> baseTypes = new List<Type>();
            GetBaseType(key, baseTypes);
            if (baseTypes.Count != 0)
            {
                foreach (var baseType in baseTypes)
                    cache.Set(baseType, value);
            }
        }

        private void GetBaseType(Type type, List<Type> baseTypes)
        {
            var baseType = type.BaseType;
            if (baseType.IsPrimitive) return;
            if (!baseType.IsGenericType) baseTypes.Add(baseType);
            var baseInterfaces = type.GetInterfaces();
            if (baseInterfaces != null)
            {
                foreach (var item in baseInterfaces)
                {
                    if (item.IsPrimitive) continue;
                    if (!item.IsGenericType) baseTypes.Add(item);
                    GetBaseType(item, baseTypes);
                }
            }
        }

        public void Set(ICacheCollection<Type, TValue> cache)
        {
            if (cache == null || cache.Count == 0) return;
            TValue value;
            var keys = cache.Keys;
            foreach (var key in keys)
            {
                if (TryGet(key, out value)) Set(key, value);
            }
        }

        public bool TryGet(Type key, out TValue value)
        {
            return cache.TryGet(key, out value);
        }

        public IEnumerator<Tuple<Type, TValue>> GetEnumerator()
        {
            return cache.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (cache as System.Collections.IEnumerable).GetEnumerator();
        }
    }
}
